1 unstable release
Uses new Rust 2024
| 0.1.0 | Nov 16, 2025 |
|---|
#14 in #structured-output
67KB
1.5K
SLoC
hefa-core
Rust 製 LLM 基盤ライブラリ。設計や詳細仕様は docs/ を参照してください。
開発手順
.env.exampleをコピーして.envを作成し、必要な API キーを設定します。cargo fmt/cargo clippy/cargo testで品質を維持します。- docs:
docs/hefa-core-spec.md要件定義docs/AGENTS.mdAgent 層仕様docs/PLAN.md実装チェックリスト
実行環境に関する注記
OPENAI_API_KEYは環境変数として既に投入済みなので.envには記載せず、そのまま利用してください。- LM Studio / Ollama は 192.168.11.16 上で稼働しています。
- LM Studio:
openai/gpt-oss-20bモデルを OpenAI 互換エンドポイント経由で利用可能。 - Ollama:
gpt-oss:20bモデルを利用可能。
- LM Studio:
トレース切替
Agent では hefa_core::Tracer を差し替えることでトレースの保存先を切り替えられます。
use hefa_core::{StdoutTracer, SqliteTracer};
let tracer = StdoutTracer::default(); // 開発時は標準出力
// もしくは永続化
let tracer = SqliteTracer::new("trace.db")?;
let agent = agent.with_tracer(std::sync::Arc::new(tracer));
SqliteTracer には list_recent_spans / list_events があり、SQLite 上に保存された span / event の検索が可能です。
LLM クライアント
hefa_core::LLMClient はプロバイダ種別に応じて自動で OpenAI Responses API / OpenAI 互換 ChatCompletion API を呼び分けます。以下は「エンジニアの日報からリリースノートのドラフトを作る」例です。
use hefa_core::{
llm::StructuredOutput, Agent, AgentConfig, ProviderKind, Tool, ToolError, ToolResult,
};
use async_trait::async_trait;
use serde_json::{json, Value};
/// Tool used when LLM asks for metadata to enrich the release note.
/// LLM が「リリースノートの下書きを作って」と要求した際に呼ばれる Hook。
struct ReleaseNoteTool;
#[async_trait]
impl Tool for ReleaseNoteTool {
fn name(&self) -> &'static str {
"release_note"
}
fn json_schema(&self) -> Value {
json!({
"type": "object",
"properties": {
"feature": { "type": "string" },
"impact": { "type": "string" }
},
"required": ["feature", "impact"]
})
}
async fn call(&self, args: Value) -> Result<ToolResult, ToolError> {
let feature = args
.get("feature")
.and_then(Value::as_str)
.ok_or_else(|| ToolError::InvalidInput("missing feature".into()))?;
let impact = args
.get("impact")
.and_then(Value::as_str)
.ok_or_else(|| ToolError::InvalidInput("missing impact".into()))?;
Ok(ToolResult {
content: json!({ "draft": format!("Feature: {feature} — Impact: {impact}") }),
})
}
}
let structured_output = StructuredOutput::new(json!({
"type": "object",
"properties": {
"summary": { "type": "string" }
}
}));
let mut agent = Agent::new(AgentConfig {
instruction: "You are a release-note assistant. Respond in 1 sentence.".into(),
provider: ProviderKind::OpenAi,
model: "gpt-4o-mini".into(),
structured_output: Some(structured_output),
tools: vec![Box::new(ReleaseNoteTool)],
})?;
let prompt = "Update: Added offline search with 50% faster indexing.";
let result = agent.invoke(prompt).await?;
println!("Release note: {}", result.response.content);
出力例:
Release note: OK — Feature: Added offline search with 50% faster indexing.
チュートリアル / Examples
-
ステップ1 – LLM 選択 & Hello
まずは Provider とモデルを決め、最小構成の Agent で “Hello” を返す例 (examples/basic_agent.rs) を実行します。cargo run --example basic_agent -
ステップ2 – Tool 追加
examples/tool_agent.rsでは LLM のtool_callsを処理する Hook を登録し、ReleaseNoteTool で外部ロジックを呼び出す流れを体験します。cargo run --example tool_agent -
ステップ3 – 構造化出力
JSON Schema を指定して LLM の最終回答を構造化する例はexamples/structured_agent.rsで確認できます。README で紹介したStructuredOutput::from_type::<T>()も併用してください。cargo run --example structured_agent -
ステップ4 – トレース
examples/trace_agent.rsはSqliteTracerを使って span / event を保存し、list_recent_spansで属性フィルタ(例:instructionに “release” が含まれる)をかけて検索する例です。Tracerにはstart_trace/start_child_span/record_eventが用意されており、Agent はこれらを内部で呼び出します。cargo run --example trace_agent -
ステップ5 – Release Note Agent (統合)
examples/release_note.rsで、Tool + 構造化出力 + トレースの統合例を確認できます。cargo run --example release_note
ライブ接続テスト
OpenAI / LM Studio / Ollama に接続する実機テストは tests/live_llm.rs にあります。
以下の環境変数をセットして個別に実行してください。
HEFA_LIVE_OPENAI=1 cargo test live_openai_responses
HEFA_LIVE_LMSTUDIO=1 LMSTUDIO_API_BASE=http://192.168.11.16:1234/v1 cargo test live_lmstudio_chat
HEFA_LIVE_OLLAMA=1 OLLAMA_API_BASE=http://192.168.11.16:11434 cargo test live_ollama_chat
構造化出力と schemars 連携
Feature schema を有効にすると、schemars::JsonSchema を derive した型から
StructuredOutput::from_type::<T>() で JSON Schema を生成できます。LLM へ構造化出力を要求する際に役立ちます。
cargo add schemars --features derive
cargo test --features schema
use hefa_core::llm::StructuredOutput;
use schemars::JsonSchema;
use serde::Serialize;
#[derive(Serialize, JsonSchema)]
struct MyAnswer {
summary: String,
tags: Vec<String>,
}
let output = StructuredOutput::from_type::<MyAnswer>();
ライセンス
TBD
Dependencies
~29–45MB
~674K SLoC