@mediconsol/agent-core
v0.1.6
Published
메디콘솔 AI Agent 코어 모듈 — 모든 메디콘솔 프로젝트에 탑재 가능한 에이전트 인프라
Maintainers
Readme
@mediconsol/agent-core
메디콘솔 AI Agent 코어 모듈 — 모든 메디콘솔 프로젝트에 탑재 가능한 에이전트 인프라
npm install @mediconsol/agent-core핵심 기능
1. MediconsolAgent — Agentic Loop
목표를 주면 도구를 사용하며 스스로 반복 추론하는 에이전트입니다.
- Anthropic Claude (
claude-opus-4-6) 기반 - Adaptive Thinking — 복잡한 추론에서 자동으로 깊이 사고
- Prompt Caching — 시스템 프롬프트 캐싱으로 API 비용 절감
- 브라우저 + Node.js 양쪽에서 동작
import { MediconsolAgent } from '@mediconsol/agent-core';
const agent = new MediconsolAgent({
apiKey: 'your-anthropic-api-key',
dangerouslyAllowBrowser: true, // 브라우저에서 실행 시
});
const result = await agent.run('이번 주 미완료 태스크 현황 알려줘', {
tools: [...],
onProgress: (text) => console.log(text),
});
console.log(result.output); // 최종 답변
console.log(result.iterations); // 반복 횟수
console.log(result.toolCalls); // 도구 호출 기록2. AgentTool — 도구 정의
에이전트가 사용할 수 있는 도구(API 호출, DB 조회 등)를 정의합니다.
import type { AgentTool } from '@mediconsol/agent-core';
const myTool: AgentTool = {
name: 'get_patient_list',
description: '환자 목록을 조회합니다.',
input_schema: {
type: 'object',
properties: {
ward: { type: 'string', description: '병동 코드' },
},
},
execute: async (input) => {
const data = await fetchPatients(input.ward as string);
return JSON.stringify(data);
},
};3. Human Check — 사람 승인 체크포인트
상태 변경·생성·삭제처럼 민감한 작업은 사람의 승인을 먼저 받습니다.
const tool: AgentTool = {
name: 'update_schedule',
requiresHumanApproval: true, // 이 플래그가 핵심
// ...
};
await agent.run('내일 일정 변경해줘', {
tools: [tool],
onHumanCheckpoint: async (checkpoint) => {
// UI 모달 등으로 사용자에게 보여주고 승인/거부 반환
return confirm(checkpoint.message);
},
});4. SkillRegistry — SOP 스킬 등록
반복되는 업무 절차(SOP)를 스킬로 등록하면 에이전트가 /스킬명 명령으로 실행합니다.
agent.skills.register({
name: 'weekly-report',
description: '주간 태스크 완료 현황을 분석하고 리포트를 생성합니다.',
inputSchema: {
type: 'object',
properties: {
week: { type: 'string', description: '리포트 대상 주 (예: 2026-W15)' },
},
required: ['week'],
},
execute: async (input, context) => {
const prev = context.memory.get<string>('last-report');
const report = `${input.week} 주간 리포트: ...`;
context.memory.persist('last-report', report);
return report;
},
});
await agent.run('/weekly-report week=2026-W15');5. AgentsConfig — 에이전트 역할 정의 (AGENTS.md 패턴)
에이전트의 이름, 역할, 행동 원칙을 구조화된 설정으로 관리합니다.
const agent = new MediconsolAgent({
apiKey: '...',
config: {
name: 'aatto_task 오케스트레이터',
description: '프로젝트 관리 및 태스크 자동화 에이전트',
targets: ['운영팀', '프로젝트 매니저'],
priority: ['정확한 태스크 정보 제공', 'Human Check (상태 변경 시)'],
doList: ['태스크 현황 조회 후 우선순위 명확히 제시'],
dontList: ['사용자 승인 없이 태스크 생성/수정/삭제'],
},
});6. Memory — 세션 + 영속 메모리
에이전트가 대화 간 정보를 기억합니다.
| 종류 | 저장소 | 유지 기간 |
|------|--------|-----------|
| SessionMemory | 메모리 (Map) | 페이지 새로고침까지 |
| PersistentMemory | localStorage | 브라우저 데이터 삭제 전까지 |
| CompositeMemory | 두 레이어 합성 | persist() 호출 시 영속 저장 |
// 에이전트 생성 시 영속 메모리 활성화
const agent = new MediconsolAgent({
apiKey: '...',
persistMemory: true,
memoryKey: 'aatto-agent',
});
// 스킬 내에서 사용
context.memory.set('key', value); // 세션에만 저장
context.memory.persist('key', value); // localStorage에도 저장
const val = context.memory.get<string>('key');7. Orchestrator — 멀티에이전트 오케스트레이션
복잡한 목표를 태스크로 분해하고, 의존성 순서에 맞춰 각 태스크를 실행합니다.
import { MediconsolAgent, Orchestrator } from '@mediconsol/agent-core';
const agent = new MediconsolAgent({ apiKey: '...' });
const orchestrator = new Orchestrator(agent);
const result = await orchestrator.run({
goal: '이번 주 업무 현황 리포트 생성',
tasks: [
{ id: 'collect', goal: '미완료 태스크 수집', tools: [listTodos] },
{ id: 'analyze', goal: '우선순위 분석', dependsOn: ['collect'] },
{ id: 'report', goal: '리포트 생성', dependsOn: ['analyze'] },
],
});
console.log(result.summary); // 최종 요약
console.log(result.tasks); // 각 태스크 결과전체 옵션
new MediconsolAgent({
apiKey: string; // Anthropic API 키 (필수)
model?: string; // 기본값: 'claude-opus-4-6'
maxTokens?: number; // 기본값: 8192
config?: AgentsConfigOptions; // 에이전트 역할 정의
persistMemory?: boolean; // localStorage 메모리 활성화
memoryKey?: string; // localStorage 키 이름
baseURL?: string; // API 엔드포인트 오버라이드
dangerouslyAllowBrowser?: boolean; // 브라우저 실행 허용
});agent.run(userMessage, {
tools?: AgentTool[]; // 사용 가능한 도구 목록
maxIterations?: number; // 최대 반복 횟수 (기본값: 20)
onProgress?: (text: string) => void; // 텍스트 스트리밍 콜백
onToolCall?: (name: string, input: unknown) => void; // 도구 호출 콜백
onHumanCheckpoint?: (cp: HumanCheckpoint) => Promise<boolean>; // Human Check 콜백
});보안 — API 키 노출 방지 (프록시 패턴)
dangerouslyAllowBrowser: true로 브라우저에서 직접 실행하면 Anthropic API 키가 클라이언트 코드에 노출됩니다. 프로덕션에서는 반드시 서버 프록시를 사용하세요.
구조
브라우저 (API 키 없음)
→ POST /api/anthropic/v1/messages
→ Next.js API Route (서버에서 ANTHROPIC_API_KEY 보관)
→ Anthropic API1. Next.js 프록시 라우트 설치
app/api/anthropic/[...path]/route.ts 생성:
import { NextRequest, NextResponse } from 'next/server';
export async function POST(
req: NextRequest,
{ params }: { params: Promise<{ path: string[] }> },
) {
const { path } = await params;
const targetUrl = `https://api.anthropic.com/v1/${path.join('/')}`;
const forwardHeaders: Record<string, string> = {
'content-type': 'application/json',
'x-api-key': process.env.ANTHROPIC_API_KEY ?? '', // 서버 환경변수
'anthropic-version': req.headers.get('anthropic-version') ?? '2023-06-01',
};
const betaHeader = req.headers.get('anthropic-beta');
if (betaHeader) forwardHeaders['anthropic-beta'] = betaHeader;
const upstream = await fetch(targetUrl, {
method: 'POST',
headers: forwardHeaders,
body: await req.text(),
});
return new NextResponse(upstream.body, {
status: upstream.status,
headers: { 'content-type': upstream.headers.get('content-type') ?? 'application/json' },
});
}2. 서버 환경변수 설정
.env.local:
ANTHROPIC_API_KEY=sk-ant-...3. 클라이언트에서 프록시 경유 에이전트 생성
const agent = new MediconsolAgent({
apiKey: 'proxy', // 더미값 (서버가 실제 키로 교체)
baseURL: '/api/anthropic', // Next.js 프록시 라우트
dangerouslyAllowBrowser: true,
});API 키는 서버(.env.local)에만 존재하며, 브라우저 네트워크 탭에도 노출되지 않습니다.
Rails 프록시
app/controllers/anthropic_proxy_controller.rb:
require 'net/http'
class AnthropicProxyController < ApplicationController
include ActionController::Live
skip_before_action :verify_authenticity_token
def proxy
target_url = URI("https://api.anthropic.com/#{params[:path]}")
api_key = Rails.application.credentials.dig(:anthropic, :api_key) ||
ENV.fetch('ANTHROPIC_API_KEY', nil)
headers = {
'Content-Type' => 'application/json',
'x-api-key' => api_key,
'anthropic-version' => request.headers['anthropic-version'] || '2023-06-01',
}
beta = request.headers['anthropic-beta']
headers['anthropic-beta'] = beta if beta.present?
Net::HTTP.start(target_url.host, target_url.port, use_ssl: true) do |http|
req = Net::HTTP::Post.new(target_url.path, headers)
req.body = request.body.read
http.request(req) do |res|
response.headers['Content-Type'] = res['content-type'] || 'application/json'
response.status = res.code.to_i
res.read_body { |chunk| response.stream.write(chunk) }
end
end
rescue ActionController::Live::ClientDisconnected
# 정상 종료
ensure
response.stream.close
end
endconfig/routes.rb:
post '/api/anthropic/*path', to: 'anthropic_proxy#proxy'Rails credentials 설정 (rails credentials:edit):
anthropic:
api_key: sk-ant-...클라이언트:
const agent = new MediconsolAgent({
apiKey: 'proxy',
baseURL: '/api/anthropic',
dangerouslyAllowBrowser: true,
});탑재 프로젝트
| 프로젝트 | 상태 |
|----------|------|
| aatto_task | 통합 예정 (examples/aatto-task/agent.ts 참고) |
라이선스
MIT © MediConsol
