{"id":51637636,"url":"https://github.com/whilesmartphp/eloquent-agents","last_synced_at":"2026-07-13T16:30:30.286Z","repository":{"id":370556474,"uuid":"1269374188","full_name":"whilesmartphp/eloquent-agents","owner":"whilesmartphp","description":"AI tool-calling foundation for Laravel: ready-made tools and an extension API over a swappable LLM engine.","archived":false,"fork":false,"pushed_at":"2026-07-09T14:18:29.000Z","size":57,"stargazers_count":0,"open_issues_count":2,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-09T16:11:01.494Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/whilesmartphp.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-14T16:27:06.000Z","updated_at":"2026-07-09T14:20:06.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/whilesmartphp/eloquent-agents","commit_stats":null,"previous_names":["whilesmartphp/eloquent-agents"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/whilesmartphp/eloquent-agents","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whilesmartphp%2Feloquent-agents","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whilesmartphp%2Feloquent-agents/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whilesmartphp%2Feloquent-agents/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whilesmartphp%2Feloquent-agents/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/whilesmartphp","download_url":"https://codeload.github.com/whilesmartphp/eloquent-agents/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whilesmartphp%2Feloquent-agents/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35429402,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-13T02:00:06.543Z","response_time":119,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-13T16:30:29.633Z","updated_at":"2026-07-13T16:30:30.279Z","avatar_url":"https://github.com/whilesmartphp.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Eloquent Agents\n\nAI tool-calling foundation for Laravel. Built on [Prism](https://github.com/prism-php/prism), it adds the\ntwo things Prism leaves to you: a set of **ready-made tools** the model can call, and a small **extension\nAPI** so your app registers its own tools, agents (\"harnesses\"), and prompts.\n\n## Features\n\n- **Batteries included.** Ships safe, generic tools: scoped Eloquent read/write, HTTP fetch, web search,\n  storage read, a clock, and a calculator.\n- **Harnesses.** A harness is a named agent: a system prompt + a tool set + a model + a step budget. Define\n  one in a config array or a class.\n- **Bring your own tools.** Implement one method, register the class, and the model can call it.\n- **Default-deny security.** Every tool runs against a `ToolContext` (the acting user). Data tools enforce an\n  allowlist of models and columns and scope every row to that user.\n- **MCP ready.** Adopt any Prism tool, including MCP server tools, through the registry.\n\n## Installation\n\n```bash\ncomposer require whilesmart/eloquent-agents\nphp artisan vendor:publish --tag=agents-config\n```\n\nSet credentials the Prism way (provider-native env vars), e.g. `GEMINI_API_KEY`, then choose a default model:\n\n```dotenv\nAGENTS_PROVIDER=gemini\nAGENTS_MODEL=gemini-2.0-flash\n```\n\n## Quick start\n\n```php\nuse Whilesmart\\Agents\\Facades\\Agents;\nuse Whilesmart\\Agents\\ValueObjects\\ToolContext;\n\n// config/agents.php\n'harnesses' =\u003e [\n    'assistant' =\u003e [\n        'prompt' =\u003e 'example-assistant',          // a prompt name or literal text\n        'tools'  =\u003e ['clock', 'calculator'],\n        'max_steps' =\u003e 5,\n    ],\n],\n\n// anywhere in the app\n$result = Agents::harness('assistant')-\u003erun(\n    'What is 1200.50 plus 300, and what was last month?',\n    ToolContext::forUser($user),\n);\n\n$result-\u003etext;       // the model's answer\n$result-\u003etoolCalls;  // what it called\n$result-\u003eusage;      // token usage\n```\n\n## Built-in tools\n\n| Name | Permission | What it does |\n|------|------------|--------------|\n| `clock` | read | Current date/time and relative anchors (start of last month, etc.). |\n| `calculator` | read | Deterministic arithmetic over `+ - * /` and parentheses. |\n| `eloquent.query` | read | List/aggregate the user's own records across an allowlist of models. |\n| `eloquent.write` | write | Create/update allowlisted models+columns, owner-scoped. Off by default. |\n| `http.fetch` | external | GET an allowlisted host, size-capped. |\n| `web.search` | external | Search via a pluggable driver (null driver by default). |\n| `storage.read` | read | List/read files on a disk, jailed to a path prefix. |\n\nThe Eloquent tools read nothing until you declare an allowlist:\n\n```php\n// config/agents.php\n'eloquent' =\u003e [\n    'allow_writes' =\u003e true,\n    'models' =\u003e [\n        'transactions' =\u003e [\n            'model' =\u003e App\\Models\\Transaction::class,\n            'owner_key' =\u003e 'user_id',                    // rows scoped to the acting user\n            'readable' =\u003e ['amount', 'type', 'description', 'created_at'],\n            'writable' =\u003e ['amount', 'type', 'description'],\n        ],\n    ],\n],\n```\n\n## Writing your own tool\n\n```php\nuse Whilesmart\\Agents\\Tools\\AbstractTool;\nuse Whilesmart\\Agents\\ValueObjects\\ParameterSpec;\nuse Whilesmart\\Agents\\ValueObjects\\ToolContext;\n\nclass CreateReminderTool extends AbstractTool\n{\n    public function name(): string { return 'create_reminder'; }\n\n    public function description(): string { return 'Create a reminder for the user.'; }\n\n    public function parameters(): array\n    {\n        return [\n            ParameterSpec::string('text', 'What to be reminded about'),\n            ParameterSpec::string('due', 'Due date (YYYY-MM-DD)'),\n        ];\n    }\n\n    public function handle(array $arguments, ToolContext $context): string|array\n    {\n        $reminder = $context-\u003euser-\u003ereminders()-\u003ecreate($arguments);\n\n        return ['id' =\u003e $reminder-\u003eid];\n    }\n}\n```\n\nRegister it by config (`'tools' =\u003e [CreateReminderTool::class]`), at runtime\n(`Agents::registerTool(new CreateReminderTool)`), or enable auto-discovery of `App\\Ai\\Tools`.\n\n## Harness as a class\n\nWhen config arrays are not enough, extend `AbstractHarness`:\n\n```php\nuse Whilesmart\\Agents\\Harness\\AbstractHarness;\nuse Whilesmart\\Agents\\Enums\\ToolPermission;\n\nclass FinanceHarness extends AbstractHarness\n{\n    public function name(): string { return 'finance'; }\n\n    public function systemPrompt(): string { return 'You are a careful finance assistant.'; }\n\n    public function toolNames(): array { return ['clock', 'calculator', 'eloquent.query']; }\n\n    public function allowedPermissions(): array { return [ToolPermission::READ]; }   // refuse write tools\n}\n```\n\n`Agents::registerHarness('finance', FinanceHarness::class)`.\n\n## Overriding prompts\n\n`run()` resolves a harness prompt by name in this order: `config('agents.prompts.{name}')`, a published file\nat `resources/vendor/agents/prompts/{name}.md`, then the package default. A value that matches no prompt is\nused as literal text.\n\n## MCP tools\n\n```php\nuse Prism\\Prism\\Tools\\LaravelMcpTool;\nuse Whilesmart\\Agents\\Engines\\Prism\\Tools\\McpTool;\n\nAgents::registerTool(McpTool::wrap(new LaravelMcpTool($mcpServerTool)));\n```\n\n## Engine abstraction\n\nPrism is a swappable layer, not a hard dependency of your tools. Everything you write (tools, harnesses,\nparameters, results) speaks the package's own types. All Prism SDK code lives behind the `AgentEngine`\ncontract under `src/Engines/Prism/`:\n\n- `PrismEngine` builds and runs the Prism request and maps the response to an `AgentResult`.\n- `PrismToolAdapter` translates a package `Tool` into a Prism tool.\n\nTo move to a different backend, implement `AgentEngine` and rebind it:\n\n```php\n// a service provider\n$this-\u003eapp-\u003ebind(\\Whilesmart\\Agents\\Contracts\\AgentEngine::class, MyEngine::class);\n```\n\nNothing else changes: tools, harnesses, registries, and the facade are untouched.\n\n## Console\n\n```bash\nphp artisan agents:tools        # list registered tools\nphp artisan agents:harnesses    # list registered harnesses\nphp artisan agents:run finance \"how much did I spend last month?\" --user=1\n```\n\n## Security model\n\nDefault-deny. Tools authorize against the `ToolContext` before running. `eloquent.query`/`eloquent.write`\nexpose nothing outside the configured model+column allowlist and scope every row to the acting user;\n`eloquent.write` additionally requires `allow_writes` and applies only whitelisted columns (mass-assignment\nguard), forcing the owner key on create. `http.fetch` honours a host allowlist; `storage.read` is jailed to a\npath prefix and rejects traversal. The agent loop is bounded by `agents.max_steps`.\n\n## Testing\n\n```bash\nmake check     # pint + phpunit via Docker\n# or\ncomposer test\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwhilesmartphp%2Feloquent-agents","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwhilesmartphp%2Feloquent-agents","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwhilesmartphp%2Feloquent-agents/lists"}