플러그인 LLM 액세스
ctx.llm은 플러그인이 LLM 호출을 수행하는 데 지원되는 방법입니다.
Chat Completions, 구조화된 추출, 동기화, 비동기(유무)
이미지 — 동일한 표면, 동일한 신뢰 게이트, 동일한 호스트 소유 자격 증명.
플러그인은 다음과 관련된 작업을 수행해야 할 때 이를 위해 도달합니다. 모델이지만 에이전트 대화의 일부가 아닙니다. 후크 엔지니어가 아닌 사람도 읽을 수 있는 내용으로 도구 오류를 다시 작성합니다. 에이 큐에 넣기 전에 인바운드 메시지를 변환하는 게이트웨이 어댑터 그것. 긴 붙여넣기를 요약하는 슬래시 명령입니다. 예약된 작업 어제의 활동을 기록하고 상태에 한 줄을 기록합니다. 보드. 메시지를 깨울 가치가 있는지 여부를 결정하는 사전 필터 에이전트가 전혀 준비되지 않았습니다.
이는 에이전트가 루프에 있어서는 안 되는 작업입니다. 그들은 하나를 원한다 LLM 통화, 답변 입력 및 완료.
가능한 가장 작은 통화
result = ctx.llm.complete(messages=[{"role": "user", "content": "ping"}])
return result.text
이것이 한 줄에 전체 API입니다. 키 없음, 제공자 구성 없음, 없음 SDK 초기화. 플러그인은 모든 제공자에 대해 실행되며 사용자가 현재 사용하고 있는 모델 — 제공자를 전환하면 플러그인은 자동으로 이를 따릅니다.
더 완전한 채팅 예시
result = ctx.llm.complete(
messages=[
{"role": "system", "content": "Rewrite errors as one short sentence a non-engineer can act on."},
{"role": "user", "content": traceback_text},
],
max_tokens=64,
purpose="hooks.error-rewrite",
)
return result.text
``purpose`은 자유 형식 감사 문자열입니다. `agent.log`에 표시됩니다.
`result.audit`에서 운영자는 어떤 플러그인이 어떤 플러그인을 만들었는지 확인할 수 있습니다.
전화. 선택 사항이지만 자주 발생하는 모든 항목에 권장됩니다.
## 구조화된 출력 \{#structured-output}
플러그인에 입력된 답변이 필요한 경우 구조화된 레인으로 전환하세요.
```python
result = ctx.llm.complete_structured(
instructions="Score this support reply for urgency (0–1) and pick a category.",
input=[{"type": "text", "text": message_body}],
json_schema=TRIAGE_SCHEMA,
purpose="support.triage",
temperature=0.0,
max_tokens=128,
)
if result.parsed["urgency"] > 0.8:
await dispatch_to_oncall(result.parsed["category"], message_body)
호스트는 공급자로부터 JSON 출력을 요청하고 이를 로컬로 구문 분석합니다.
대체적으로 jsonschema이 다음과 같은 경우 스키마에 대해 유효성을 검사합니다.
설치하고 result.parsed에 Python 개체를 반환합니다. 만약
모델이 유효한 JSON을 생성할 수 없습니다. result.parsed은 None이며
result.text은 원시 응답을 전달합니다.
이 차선이 당신에게 주는 것
- 한 번의 통화, 네 가지 형태. 채팅의 경우
complete(),complete_structured()(입력된 JSON의 경우),acomplete()및 asyncio의 경우acomplete_structured(). 같은 주장, 같은 결과 객체. - 호스트 소유 자격 증명. OAuth 토큰, 새로 고침 흐름,
자격 증명 풀, 작업별 보조 재정의 — 모든 자격 증명
헤르메스 개념은 이미 적용되어 있습니다. 플러그인은 결코
토큰; 호스트는
result.audit을 통해 콜백을 속성화합니다. - 제한됨. 단일 동기화 또는 비동기 호출. 스트리밍 없음, 도구 없음 루프, 관리할 대화 상태가 없습니다. 입력을 명시하고, 결과, 반환.
- 페일클로즈 신뢰. 구성한 적이 없는 플러그인은
자체 공급자, 모델, 에이전트 또는 저장된 자격 증명을 선택합니다. 그만큼
기본 자세는 "사용자가 사용하는 것을 사용합니다."입니다. 운영자가 동의함
config.yaml에서 플러그인별로 특정 재정의를 수행합니다.
빠른 시작
아래에는 두 개의 완전한 플러그인이 있습니다. 하나는 채팅이고 다른 하나는 구조화되어 있습니다. 두 선박 모두
단일 register(ctx) 함수 내부에 있고 외부에는 0이 필요합니다.
사용자가 활성화한 모든 모델에 대해 실행되도록 구성합니다.
채팅 완료 — /tldr
def register(ctx):
ctx.register_command(
name="tldr",
handler=lambda raw: _tldr(ctx, raw),
description="Summarise the supplied text in one paragraph.",
args_hint="<text>",
)
def _tldr(ctx, raw_args: str) -> str:
text = raw_args.strip()
if not text:
return "Usage: /tldr <text to summarise>"
result = ctx.llm.complete(
messages=[
{"role": "system",
"content": "Summarise the user's text in one tight paragraph. No preamble."},
{"role": "user", "content": text},
],
max_tokens=256,
temperature=0.3,
purpose="tldr",
)
return result.text
``result.text`은 모델의 응답입니다. `result.usage`은 토큰을 운반합니다.
카운트; `result.provider` 및 `result.model`에는 속성이 있습니다.
### 구조화된 추출 — `/paste-to-tasks` \{#structured-output}
```python
def register(ctx):
ctx.register_command(
name="paste-to-tasks",
handler=lambda raw: _paste_to_tasks(ctx, raw),
description="Turn freeform meeting notes into structured tasks.",
args_hint="<text>",
)
_TASKS_SCHEMA = {
"type": "object",
"properties": {
"tasks": {
"type": "array",
"items": {
"type": "object",
"properties": {
"owner": {"type": "string"},
"action": {"type": "string"},
"due": {"type": "string", "description": "ISO date or empty"},
},
"required": ["action"],
},
},
},
"required": ["tasks"],
}
def _paste_to_tasks(ctx, raw_args: str) -> str:
if not raw_args.strip():
return "Usage: /paste-to-tasks <meeting notes>"
result = ctx.llm.complete_structured(
instructions=(
"Extract concrete action items from these meeting notes. "
"One task per actionable line. If no owner is named, leave 'owner' blank."
),
input=[{"type": "text", "text": raw_args}],
json_schema=_TASKS_SCHEMA,
schema_name="meeting.tasks",
purpose="paste-to-tasks",
temperature=0.0,
max_tokens=512,
)
if result.parsed is None:
return f"Couldn't parse a response. Raw output:\n{result.text}"
lines = [f"- [{t.get('owner') or '?'}] {t['action']}" for t in result.parsed["tasks"]]
return "\n".join(lines) or "(no tasks found)"
세 번째 작업 예제는 이번에는 이미지 입력을 사용하여
hermes-example-plugins
repo(참조 플러그인을 위한 동반 저장소 - 번들로 제공되지 않음)
헤르메스 에이전트 자체). 비동기 표면의 경우(acomplete() /
acomplete_structured() 및 asyncio.gather()), 참조
plugin-llm-async-example
같은 저장소에 있습니다.
언제 어느 것을 사용할지
| 당신이 원하는… | 도달하다 |
|---|---|
| 자유 형식 텍스트 응답(번역, 요약, 재작성, 생성) | complete() |
| 다중 턴 프롬프트(시스템 + 몇 장의 예시 + 사용자) | complete() |
| 스키마에 대해 검증된 입력된 사전 | complete_structured() |
| 입력된 사전을 사용한 이미지 또는 텍스트 입력 | complete_structured() |
| 비동기 코드(게이트웨이 어댑터, 비동기 후크)의 동일한 호출 | acomplete() / acomplete_structured() |
그 밖의 모든 것 - 제공자 선택, 모델 확인, 인증, 대체, 시간 초과, 비전 라우팅 — 네 가지 모두에서 동일합니다.
API 표면
ctx.llm은 agent.plugin_llm.PluginLlm의 인스턴스입니다.
complete()
result = ctx.llm.complete(
messages=[{"role": "user", "content": "Hi"}],
provider=None, # optional, gated — Hermes provider id (e.g. "openrouter")
model=None, # optional, gated — whatever string that provider expects
temperature=None,
max_tokens=None,
timeout=None, # seconds
agent_id=None, # optional, gated
profile=None, # optional, gated — explicit auth-profile name
purpose="optional-audit-string",
)
# → PluginLlmCompleteResult(text, provider, model, agent_id, usage, audit)
일반 Chat Completions. messages은 표준 OpenAI 형태입니다.
{"role": "...", "content": "..."} 사전 목록. 다회전
프롬프트(시스템 + 소수 사용자/보조 쌍 + 최종 사용자) 작업
OpenAI SDK와 똑같습니다.
provider= 및 model=은 독립적이며 동일한 모양을 따릅니다.
호스트의 기본 구성(model.provider + model.model)으로. 세트
사용자의 활성 제공자를 다른 제공자와 함께 사용하려면 model=만 사용하세요.
그것에 모델. 제공자를 완전히 전환하려면 둘 다 설정하세요. 어느 주장이든
운영자 동의 없이 PluginLlmTrustError이 발생합니다.
complete_structured()
result = ctx.llm.complete_structured(
instructions="What you want extracted.",
input=[
{"type": "text", "text": "..."},
{"type": "image", "data": b"...", "mime_type": "image/png"},
{"type": "image", "url": "https://..."},
],
json_schema={...}, # optional — triggers parsed result + validation
json_mode=False, # set True without a schema to ask for JSON anyway
schema_name=None, # optional human-readable schema name
system_prompt=None,
provider=None, # optional, gated
model=None, # optional, gated
temperature=None,
max_tokens=None,
timeout=None,
agent_id=None,
profile=None,
purpose=None,
)
# → PluginLlmStructuredResult(text, provider, model, agent_id,
# usage, parsed, content_type, audit)
입력은 입력된 텍스트 또는 이미지 블록입니다(원시 바이트는 base64로 인코딩됨).
자동으로 data: URL로). json_schema 또는
json_mode=True이 제공되면 호스트는 다음을 통해 JSON 출력을 요청합니다.
response_format, 대체 수단으로 로컬에서 구문 분석하고 유효성을 검사합니다.
jsonschema이 설치된 경우 스키마에 대해.
result.content_type == "json"—result.parsed은 Python입니다. 스키마와 일치하는 개체입니다.result.content_type == "text"— 구문 분석 또는 유효성 검사에 실패했습니다. 원시 모델 응답을 보려면result.text을 검사하세요.
비동기
result = await ctx.llm.acomplete(messages=...)
result = await ctx.llm.acomplete_structured(instructions=..., input=...)
해당 동기화 항목과 동일한 인수 및 결과 유형입니다. 사용 게이트웨이 어댑터, 비동기 후크 또는 플러그인 코드에서 가져온 것입니다. 이미 asyncio 루프에서 실행 중입니다.
결과 속성
@dataclass
class PluginLlmCompleteResult:
text: str # the assistant's response
provider: str # e.g. "openrouter", "anthropic"
model: str # whatever the provider returned for this call
agent_id: str # whose model/auth was used
usage: PluginLlmUsage # tokens + cache + cost estimate
audit: Dict[str, Any] # plugin_id, purpose, profile
@dataclass
class PluginLlmStructuredResult(PluginLlmCompleteResult):
parsed: Optional[Any] # JSON object when content_type == "json"
content_type: str # "json" or "text"
# audit also carries schema_name when supplied
``usage`은 `input_tokens`, `output_tokens`, `total_tokens`을 전달합니다.
`cache_read_tokens`, `cache_write_tokens` 및 `cost_usd`
공급자는 해당 필드를 반환합니다.
## 트러스트 게이트 \{#trust-gate}
기본 동작은 장애 시 닫힘입니다. `plugins.entries` 없음
config 블록을 사용하면 플러그인이 다음을 수행할 수 있습니다.
* 사용자의 활성 공급자에 대해 네 가지 방법 중 하나를 실행합니다.
그리고 모델,
* 요청 형성 인수 설정(`temperature`, `max_tokens`,
`timeout`, `system_prompt`, `purpose`, `messages`, `instructions`,
`input`, `json_schema`),
…그리고 그게 다야. `provider=`, `model=`, `agent_id=` 및 `profile=`
인수는 운영자가 선택할 때까지 `PluginLlmTrustError`을 발생시킵니다.
**대부분의 플러그인에는 이 섹션이 필요하지 않습니다.** 단지 호출만 하는 플러그인
재정의가 없는 `ctx.llm.complete(messages=...)`이 실행됩니다.
사용자가 활성 상태이고 구성 없이 작동하는 모든 것. 아래 블록
플러그인이 특별히 특정 항목에 고정하려는 경우에만 관련이 있습니다.
사용자와 모델이나 제공업체가 다릅니다.
```yaml
plugins:
entries:
my-plugin:
llm:
# Allow this plugin to choose a different Hermes provider
# (must be one Hermes already knows about — same names as
# `hermes model` and config.yaml model.provider).
allow_provider_재정의: true
# Optionally restrict which providers. Use ["*"] for any.
allowed_providers:
- openrouter
- anthropic
# Allow this plugin to ask for a specific model.
allow_model_재정의: true
# Optionally restrict which models. Use ["*"] for any.
# Models are matched literally against whatever string the
# plugin sends — Hermes does not look anything up.
allowed_models:
- openai/gpt-4o-mini
- anthropic/claude-3-5-haiku
# Allow cross-agent calls (rare).
allow_agent_id_재정의: false
# Allow the plugin to request a specific stored auth profile
# (e.g. a different OAuth account on the same provider).
allow_profile_재정의: false
플러그인 ID는 플랫 플러그인의 매니페스트 name: 필드이거나
중첩된 플러그인에 대한 경로 파생 키(image_gen/openai,
memory/honcho 등).
게이트가 시행하는 것
| 재정의 | 기본값 | 구성 키 |
|---|---|---|
provider= | 거부됨 | allow_provider_override: true |
| ↳ 허용 목록 | — | allowed_providers: [...] |
model= | 거부됨 | allow_model_override: true |
| ↳ 허용 목록 | — | allowed_models: [...] |
agent_id= | 거부됨 | allow_agent_id_override: true |
profile= | 거부됨 | allow_profile_override: true |
각 재정의는 독립적으로 게이트됩니다. allow_model_override 부여
allow_provider_override도 부여하지 않습니다 — 신뢰할 수 있는 플러그인
모델을 선택하는 것은 여전히 사용자의 활성 공급자에 고정되어 있습니다.
공급자 게이트도 가져옵니다.
게이트가 시행할 필요가 없는 것
- 요청 구성 인수 —
temperature,max_tokens,timeout,system_prompt,purpose,messages,instructions,input,json_schema,schema_name,json_mode— 항상 허용됨; 자격 증명이나 경로를 선택하지 않습니다. - 기본 거부 상태는 구성되지 않은 플러그인이 여전히 거부할 수 있음을 의미합니다.
유용한 작업 - 활성 공급자 및 모델에 대해서만 실행됩니다.
운영자는 플러그인에 대해
plugins.entries만 생각하면 됩니다. 더 미세한 라우팅을 원하는 경우
호스트가 소유한 것
ctx.llm이 플러그인에 대해 수행하는 작업의 전체 목록입니다.
그럴 필요는 없습니다:
- 공급자 해결.
model.provider+model.model을 읽습니다. 사용자 구성(또는 신뢰할 수 있는 경우 명시적 재정의)에서 가져옵니다. - 인증. API 키, OAuth 토큰 또는 새로 고침 토큰을 가져옵니다.
~/.hermes/auth.json/ env, 자격 증명 풀 포함 하나가 구성되었습니다. 플러그인은 이를 결코 볼 수 없습니다. - 비전 라우팅. 이미지 입력이 제공되고 사용자의 활성 텍스트 모델은 텍스트 전용이므로 호스트는 비전 모델을 자동으로 구성합니다.
- 대체 체인. 사용자의 기본 공급자가 5xxs 또는 429s인 경우, 요청은 Hermes의 일반적인 수집자 인식 폴백을 거칩니다. 플러그인에 오류를 반환하기 전에.
- 시간 초과.
timeout=인수를 존중하고 다음으로 돌아갑니다.auxiliary.<task>.timeout구성 또는 전역 보조 기본값입니다. - JSON 형성. 다음과 같은 경우
response_format을 공급자에게 보냅니다. JSON을 요청한 다음 코드 펜스에서 로컬로 다시 구문 분석합니다. 공급자가 하나를 반환한 경우 응답합니다. - 스키마 유효성 검사. 다음과 같은 경우
json_schema에 대해 유효성을 검사합니다.jsonschema이 설치되었습니다. 디버그 라인을 기록하고 엄격하게 건너뜁니다. 그렇지 않으면 검증. - 감사 로그. 각 호출은
agent.log에 하나의 INFO 줄을 씁니다. 플러그인 ID, 공급자/모델, 목적 및 토큰 합계.
플러그인이 소유하는 것
- 형태를 요청합니다. 채팅의 경우
messages,instructions+input구조화를 위해. 플러그인은 프롬프트를 작성합니다. 호스트가 실행합니다. - 스키마. 원하는 모양이 무엇이든 되돌려 받을 수 있습니다. 호스트는 추론하지 않습니다. 당신을 위한 것입니다.
- 오류 처리.
complete_structured()은ValueError을 발생시킵니다. 빈 입력 및 스키마 유효성 검사 실패 시.PluginLlmTrustError신뢰 게이트가 재정의를 거부하면 실행됩니다. 다른 것 (공급자 5xx, 구성된 자격 증명 없음, 시간 초과)auxiliary_client.call_llm()인상. - 비용. 모든 호출은 사용자의 유료 공급자를 대상으로 실행됩니다. 하지 않다
생각하지 않고 모든 게이트웨이 메시지에 대해
complete()을 반복합니다. 토큰 지출에 대해.
이것이 플러그인 표면에 맞는 위치
기존 ctx.* 메서드는 기존 Hermes 하위 시스템을 확장합니다.
| ctx.register_tool | 상담원이 호출할 수 있는 도구를 추가합니다. |
| ctx.register_platform | 새 게이트웨이 어댑터 연결 |
| ctx.register_image_gen_provider | image-gen 백엔드를 대체합니다. |
| ctx.register_memory_provider | 메모리 백엔드를 대체합니다. |
| ctx.register_context_engine | 컨텍스트 압축기를 대체합니다. |
| ctx.register_hook | 수명주기 이벤트를 관찰합니다. |
ctx.llm은 플러그인이 동일하게 실행되도록 하는 첫 번째 표면입니다.
사용자가 대역 외에서 대화하고 있는 모델
위. 그것이 유일한 직업입니다. 플러그인을 등록해야 하는 경우
에이전트가 호출하는 도구인 경우 register_tool을 사용하세요. 반응이 필요한 경우
수명 주기 이벤트에는 register_hook을 사용하세요. 그것을 만들어야 하는 경우
자체 모델 호출 — 구조적이든 아니든 어떤 이유로든 — ctx.llm.
참고자료
- 구현:
agent/plugin_llm.py - 테스트:
tests/agent/test_plugin_llm.py - 참조 플러그인(동반 저장소):
plugin-llm-example— 이미지 입력과 구조화된 추출 동기화plugin-llm-async-example—asyncio.gather()과 비동기
- 보조 클라이언트(후드 아래의 엔진): 참조 공급자 런타임.