エージェント
エージェント
Section titled “エージェント”wippy/agent モジュールは、ツール使用、ストリーミング、デリゲーション、トレイト、メモリを備えた AI エージェントを構築するためのフレームワークを提供します。エージェントは宣言的に定義され、コンテキスト/ランナーパターンで実行されます。
セットアップ
Section titled “セットアップ”プロジェクトにモジュールを追加します:
wippy add wippy/agentwippy installエージェントモジュールには wippy/llm とプロセスホストが必要です。両方の依存関係を宣言します:
version: "1.0"namespace: app
entries: - name: os_env kind: env.storage.os
- name: processes kind: process.host lifecycle: auto_start: true
- name: dep.llm kind: ns.dependency component: wippy/llm version: "*" parameters: - name: env_storage value: app:os_env - name: process_host value: app:processes
- name: dep.agent kind: ns.dependency component: wippy/agent version: "*" parameters: - name: process_host value: app:processesエージェント定義
Section titled “エージェント定義”エージェントは meta.type: agent.gen1 を持つレジストリエントリです:
entries: - name: assistant kind: registry.entry meta: type: agent.gen1 name: assistant title: Assistant comment: A helpful chat assistant prompt: | You are a helpful assistant. Be concise and direct. Answer questions clearly. model: gpt-4o max_tokens: 1024 temperature: 0.7エージェントフィールド
Section titled “エージェントフィールド”| フィールド | 型 | 説明 |
|---|---|---|
meta.type | string | agent.gen1 である必要があります |
meta.name | string | エージェント識別子 |
prompt | string | システムプロンプト |
model | string | モデル名またはクラス |
max_tokens | number | 最大出力トークン数(デフォルト 512) |
temperature | number | サンプリング温度(デフォルト 0;範囲はプロバイダ依存) |
thinking_effort | number | > 0 の場合のみモデルに転送(プロバイダ定義のスケール) |
tools | array | ツールレジストリ ID |
traits | array | トレイト参照 |
delegates | array | デリゲートエージェント参照 |
memory | array | 静的メモリ項目(文字列) |
memory_contract | table | 動的メモリ設定 |
エージェントコンテキスト
Section titled “エージェントコンテキスト”エージェントコンテキストはメインのエントリポイントです。コンテキストを作成し、必要に応じて設定してから、エージェントをロードします:
imports: agent_context: wippy.agent:contextlocal agent_context = require("agent_context")
local ctx = agent_context.new()local runner, err = ctx:load_agent("app:assistant")if err then error("Failed to load agent: " .. tostring(err))endコンテキストメソッド
Section titled “コンテキストメソッド”| メソッド | 説明 |
|---|---|
agent_context.new(options?) | 新しいコンテキストを作成 |
:add_tools(specs) | 実行時にツールを追加 |
:add_delegates(specs) | デリゲートエージェントを追加 |
:configure_delegate_tools(config) | デリゲートをツールとして公開する方法を設定 |
:set_memory_contract(config) | 動的メモリを設定 |
:set_context_merger(fn) | 実行時コンテキスト更新をマージする関数を提供 |
:update_context(updates) | 実行時コンテキストを更新 |
:load_agent(spec_or_id, options?) | エージェントをロードしてコンパイル、ランナーを返す |
:switch_to_agent(id, options?) | 別のエージェントに切り替え、(boolean, string?) を返す |
:switch_to_model(name) | 現在のエージェントのモデルを変更、(boolean, string?) を返す |
:get_current_agent() | 現在のランナーを取得 |
:get_config() | コンテキスト設定の要約を返す |
コンテキストオプション
Section titled “コンテキストオプション”local ctx = agent_context.new({ context = { session_id = "abc", user_id = "u1" }, delegate_tools = { enabled = true }, enable_cache = true,})| オプション | 説明 |
|---|---|
context | ツールとデリゲートに転送されるベース実行時コンテキスト |
delegate_tools | デフォルトのデリゲートツール設定(configure_delegate_tools で上書き可能) |
enable_cache | プロンプトキャッシュマーカーを有効化(Claude モデル)。デフォルトは true。 |
インラインスペックによるロード
Section titled “インラインスペックによるロード”レジストリエントリなしでエージェントをロードします:
local runner, err = ctx:load_agent({ id = "inline-agent", name = "helper", prompt = "You are a helpful assistant.", model = "gpt-4o", max_tokens = 1024, tools = { "app.tools:search" },})ステップの実行
Section titled “ステップの実行”ランナーは単一の推論ステップを実行します。会話を含むプロンプトビルダーを渡します:
local prompt = require("prompt")
local conversation = prompt.new()conversation:add_user("What is the capital of France?")
local response, err = runner:step(conversation)if err then error(tostring(err))end
print(response.result)ステップオプション
Section titled “ステップオプション”local response, err = runner:step(conversation, { context = { session_id = "abc" }, stream_target = { reply_to = process.pid(), topic = "stream" }, tool_call = "auto",})| オプション | 型 | 説明 |
|---|---|---|
context | table | エージェントコンテキストにマージされる実行時コンテキスト |
stream_target | table | ストリーミング: { reply_to, topic } |
tool_call | string | "auto"、"any"、"none" またはツール名 |
ステップレスポンス
Section titled “ステップレスポンス”| フィールド | 型 | 説明 |
|---|---|---|
result | string | 生成されたテキスト |
tokens | table | トークン使用量 |
finish_reason | string | 停止理由 |
tool_calls | table? | 実行すべきツール呼び出し |
delegate_calls | table? | デリゲート呼び出し |
ランナー統計
Section titled “ランナー統計”local stats = runner:get_stats()-- stats.id, stats.name, stats.total_tokensツールは meta.type: tool を持つ function.lua エントリです。別の _index.yaml で定義します:
version: "1.0"namespace: app.tools
entries: - name: calculate kind: function.lua meta: type: tool title: Calculate input_schema: | { "type": "object", "properties": { "expression": { "type": "string", "description": "Math expression to evaluate" } }, "required": ["expression"], "additionalProperties": false } llm_alias: calculate llm_description: Evaluate a mathematical expression. source: file://calculate.lua modules: [expr] method: handlerlocal expr = require("expr")
local function handler(args) local result, err = expr.eval(args.expression) if err then return { error = tostring(err) } end return { result = result }end
return { handler = handler }ツールメタデータ
Section titled “ツールメタデータ”| フィールド | 型 | 説明 |
|---|---|---|
meta.type | string | tool である必要があります |
meta.input_schema | string/table | ツール引数の JSON Schema |
meta.llm_alias | string | LLM に公開される名前 |
meta.llm_description | string | LLM に公開される説明 |
meta.exclusive | boolean | true の場合、並行するツール呼び出しをキャンセル |
エージェントでのツール参照
Section titled “エージェントでのツール参照”エージェント定義にツールレジストリ ID をリストします:
- name: assistant kind: registry.entry meta: type: agent.gen1 name: assistant prompt: You are a helpful assistant with tools. model: gpt-4o max_tokens: 1024 tools: - app.tools:calculate - app.tools:search - app.tools:* # wildcard: all tools in namespaceカスタムエイリアスとコンテキストを指定してツールを参照することもできます:
tools: - id: app.tools:search alias: web_search context: api_key: "${SEARCH_API_KEY}"エージェントステップが tool_calls を返した場合、それを実行して結果をフィードバックします:
local json = require("json")local funcs = require("funcs")
local function execute_and_continue(runner, conversation) while true do local response, err = runner:step(conversation) if err then return nil, err end
local tool_calls = response.tool_calls if not tool_calls or #tool_calls == 0 then return response.result, nil end
for _, tc in ipairs(tool_calls) do local result, call_err = funcs.call(tc.registry_id, tc.arguments) local result_str if call_err then result_str = json.encode({ error = tostring(call_err) }) else result_str = json.encode(result) end
conversation:add_function_call(tc.name, tc.arguments, tc.id) conversation:add_function_result(tc.name, result_str, tc.id) end endendツール呼び出しフィールド
Section titled “ツール呼び出しフィールド”| フィールド | 型 | 説明 |
|---|---|---|
id | string | 一意の呼び出し識別子 |
name | string | ツール名(エイリアスまたは llm_alias) |
arguments | table | パース済み引数 |
registry_id | string | funcs.call() 用の完全なレジストリ ID |
funcs.call(tc.registry_id, tc.arguments) を使用します。registry_id フィールドは、レジストリ内のツールのエントリに直接対応します。
ストリーミング
Section titled “ストリーミング”stream_target を使用してエージェントのレスポンスをリアルタイムでストリーミングします:
local TOPIC = "agent_stream"
local function stream_step(runner, conversation) local stream_ch = process.listen(TOPIC)
local done_ch = channel.new(1) coroutine.spawn(function() local response, err = runner:step(conversation, { stream_target = { reply_to = process.pid(), topic = TOPIC, }, }) done_ch:send({ response = response, err = err }) end)
local full_text = "" while true do local result = channel.select({ stream_ch:case_receive(), done_ch:case_receive(), }) if not result.ok then break end
if result.channel == done_ch then process.unlisten(stream_ch) local r = result.value return full_text, r.response, r.err end
local chunk = result.value if chunk.type == "chunk" then io.write(chunk.content or "") full_text = full_text .. (chunk.content or "") elseif chunk.type == "done" then -- wait for the step to complete local r, ok = done_ch:receive() process.unlisten(stream_ch) if ok and r then return full_text, r.response, r.err end return full_text, nil, nil end end
process.unlisten(stream_ch) return full_text, nil, nilendストリームは直接 LLM ストリーミングと同じチャンクタイプを使用します: "chunk"、"thinking"、"tool_call"、"error"、"done"。
coroutine.spawn を使用して runner:step() を別のコルーチンで実行し、ストリームチャンクを並行して受信できるようにします。channel.select を使用してストリームチャネルと完了チャネルを多重化します。
エージェントは他のエージェントにデリゲートできます。デリゲートは親エージェントにとってツールとして表示されます:
- name: coordinator kind: registry.entry meta: type: agent.gen1 name: coordinator prompt: Route questions to the right specialist. model: gpt-4o max_tokens: 1024 delegates: - id: app:code_agent name: ask_coder rule: for programming questions - id: app:math_agent name: ask_mathematician rule: for math problemsデリゲート呼び出しは response.delegate_calls に含まれます:
local response = runner:step(conversation)
if response.delegate_calls then for _, dc in ipairs(response.delegate_calls) do -- dc.agent_id - target agent registry ID -- dc.name - delegate tool name -- dc.arguments - forwarded message endend実行時にデリゲートを追加することもできます:
ctx:add_delegates({ { id = "app:specialist", name = "ask_specialist", rule = "for domain questions" },})トレイトは、プロンプト、ツール、動作をエージェントに提供する再利用可能なケイパビリティです:
- name: assistant kind: registry.entry meta: type: agent.gen1 name: assistant prompt: You are a helpful assistant. model: gpt-4o traits: - time_aware - id: custom_trait context: key: value組み込みトレイト
Section titled “組み込みトレイト”| トレイト | 説明 |
|---|---|
time_aware | 現在の日付と時刻をプロンプトに注入 |
time_aware トレイトはコンテキストオプションを受け付けます:
traits: - id: time_aware context: timezone: America/New_York time_interval: 15カスタムトレイト
Section titled “カスタムトレイト”トレイトは meta.type: agent.trait を持つレジストリエントリです。以下を提供できます:
- prompt - システムプロンプトに追加される静的テキスト
- build_func_id - コンパイル時に呼び出され、ツール、プロンプト、デリゲートを提供する関数
- prompt_func_id - 各ステップで呼び出され、動的コンテンツを注入する関数
- step_func_id - 各ステップで副作用のために呼び出される関数
システムプロンプトに追加されるシンプルなメモリ項目:
- name: assistant kind: registry.entry meta: type: agent.gen1 name: assistant prompt: You are a helpful assistant. model: gpt-4o memory: - "User prefers concise answers" - "Always cite sources when possible"動的メモリコントラクト
Section titled “動的メモリコントラクト”外部ソースからの動的メモリ呼び出しを設定します:
memory_contract: implementation_id: app:memory_store context: user_id: "${user_id}" options: max_items: 3 max_length: 1000 recall_cooldown: 1 min_conversation_length: 2メモリコントラクトは runner:step() の実行中に呼び出され、会話コンテキストに基づいて関連する項目を呼び出します。結果は開発者メッセージとして注入されます。
| オプション | デフォルト | 説明 |
|---|---|---|
max_items | 3 | 1回の呼び出しあたりの最大メモリ項目数 |
max_length | 1000 | 最大合計文字数 |
recall_cooldown | 1 | 呼び出し間の最小ステップ数 |
min_conversation_length | 2 | 最初の呼び出しまでの最小会話ターン数 |
リゾルバーコントラクト
Section titled “リゾルバーコントラクト”load_agent() が文字列識別子を受け取ると、まず wippy.agent:resolver コントラクトによる解決を試みます。リゾルバーがバインドされていないか、リゾルバーが nil を返した場合、レジストリルックアップにフォールバックします。
これにより、アプリケーションはデータベースからエージェント定義をロードするなど、カスタムのエージェント解決を実装できます。
リゾルバーのバインド
Section titled “リゾルバーのバインド”リゾルバー関数を定義し、コントラクトにバインドします:
entries: - name: agent_resolver.resolve kind: function.lua source: file://agent_resolver.lua method: resolve modules: - logger imports: agent_registry: wippy.agent.discovery:registry
- name: agent_resolver_binding kind: contract.binding contracts: - contract: wippy.agent:resolver default: true methods: resolve: app:agent_resolver.resolveリゾルバーの実装
Section titled “リゾルバーの実装”リゾルバーは { agent_id = "..." } を受け取り、エージェントスペックテーブルまたは nil を返します:
local agent_registry = require("agent_registry")
local CUSTOM_PREFIX = "custom:"
function resolve(args) local agent_id = args.agent_id if not agent_id then return nil, "agent_id is required" end
if agent_id:sub(1, #CUSTOM_PREFIX) == CUSTOM_PREFIX then local id = agent_id:sub(#CUSTOM_PREFIX + 1)
-- load from database, config file, or any other source return { id = agent_id, name = "custom-agent", prompt = "You are a custom agent.", model = "class:balanced", max_tokens = 1024, tools = {}, } end
-- fall back to registry local spec, err = agent_registry.get_by_id(agent_id) if not spec then spec, err = agent_registry.get_by_name(agent_id) end return spec, errend
return { resolve = resolve,}wippy.agent:resolverコントラクトを試行(バインドされている場合)- ID によるレジストリルックアップを試行
- 名前によるレジストリルックアップを試行
- 見つからない場合はエラーを返す
このパターンにより、エージェントがユーザーごとまたはワークスペースごとに設定され、フレームワークのレジストリ外に保存されるマルチテナントアプリケーションが可能になります。
- LLM - 基盤となる LLM モジュール
- LLM エージェントの構築 - ステップバイステップのチュートリアル
- フレームワーク概要 - フレームワークモジュールの使用方法